package com.example.xjt.restful;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * 根据需要，此处可以全局处理- 各种异常应该返回的 code/msg信息
 */
@Slf4j
@RestControllerAdvice(annotations = RestController.class)
public class GlobalExceptionHandler implements ResponseBodyAdvice<Object> {

    //---------------throw出来的自定义异常,业务不符合xxx不能往下执行的,代码里直接throw的异常,throw出来后，不走原有的return逻辑了-------------------------------------------------

    /**
     * 运行时业务异常,处理代码里的 throw new APIException(XXX);
     *
     * @param e APIException
     * @return String
     */
    @ExceptionHandler({APIException.class})
    public ResultVO APIExceptionHandler(APIException e, HttpServletRequest request) {
        System.err.println(request.getHeader("lang"));
        System.err.println(e.getCode());
        if (StrUtil.isBlank(request.getHeader("lang"))) {
            return ResultVO.exception(e.getCode(), ResultCodeEnum.getZhMsgByCode(e.getCode()));
        } else if (StrUtil.equals("zh-CN", request.getHeader("lang"))) {
            return ResultVO.exception(e.getCode(), ResultCodeEnum.getZhMsgByCode(e.getCode()));
        } else if (StrUtil.equals("en-US", request.getHeader("lang"))) {
            return ResultVO.exception(e.getCode(), ResultCodeEngEnum.getEngMsgByCode(e.getCode()));
        } else {
            return ResultVO.exception(e.getCode(), ResultCodeEnum.getZhMsgByCode(e.getCode()));
        }
    }


    //-------------一些受检异常，参数没传/参数错误/除0异常/数组越界......(不tryCatch系统也能报出来的异常,若你的代码catch了受检异常，就不会走全局异常处理了)---------------------------------------------------------------------------

    /**
     * 对 jsr303 参数校验失败抛出的MethodArgumentNotValidException全局处理
     *
     * @param e MethodArgumentNotValidException
     * @return String
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResultVO MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e, HttpServletRequest request) {
        // 从异常对象中拿到ObjectError对象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 然后提取错误提示信息进行返回
        if (StrUtil.isBlank(request.getHeader("lang"))) {
            return ResultVO.exception(ResultCodeEnum.VALIDATE_FAILED, objectError.getDefaultMessage());
        } else if (StrUtil.equals("zh-CN", request.getHeader("lang"))) {
            return ResultVO.exception(ResultCodeEnum.VALIDATE_FAILED, objectError.getDefaultMessage());
        } else if (StrUtil.equals("en-US", request.getHeader("lang"))) {
            return ResultVO.exceptionEng(ResultCodeEngEnum.ARITHMETIC_FAILED, objectError.getDefaultMessage());
        } else {
            return ResultVO.exception(ResultCodeEnum.VALIDATE_FAILED, objectError.getDefaultMessage());
        }
    }


    /**
     * @param e HttpMessageNotReadableException
     * @return ResultVO
     * @RequestBody @Valid put/post请求体缺少异常
     */
    @ExceptionHandler({HttpMessageNotReadableException.class})
    public ResultVO reqBodyIsNullException(HttpMessageNotReadableException e, HttpServletRequest request) {

        if (StrUtil.isBlank(request.getHeader("lang"))) {
            return ResultVO.exception(ResultCodeEnum.REQ_BODY_IS_NULL, e);
        } else if (StrUtil.equals("zh-CN", request.getHeader("lang"))) {
            return ResultVO.exception(ResultCodeEnum.REQ_BODY_IS_NULL, e);
        } else if (StrUtil.equals("en-US", request.getHeader("lang"))) {
            return ResultVO.exceptionEng(ResultCodeEngEnum.REQ_BODY_IS_NULL, e);
        } else {
            return ResultVO.exception(ResultCodeEnum.REQ_BODY_IS_NULL, e);
        }
    }


    //@ExceptionHandler的方法入参支持：Exception ；SessionAttribute 、 RequestAttribute注解 ； HttpServletRequest  、HttpServletResponse、HttpSession.
    //
    //@ExceptionHandler方法返回值常见的可以是: ModelAndView 、@ResponseBody注解、ResponseEntity

    /**
     * 处理 ArithmeticException 类的异常 （如除0异常）
     */
    @ExceptionHandler({ArithmeticException.class})
    public ResultVO arithmeticExceptionHandler(ArithmeticException e, HttpServletRequest request) {
        System.err.println(request.getHeader("lang"));
        if (StrUtil.isBlank(request.getHeader("lang"))) {
            return ResultVO.exception(ResultCodeEnum.ARITHMETIC_FAILED, e);
        } else if (StrUtil.equals("zh-CN", request.getHeader("lang"))) {
            return ResultVO.exception(ResultCodeEnum.ARITHMETIC_FAILED, e);
        } else if (StrUtil.equals("en-US", request.getHeader("lang"))) {
            return ResultVO.exceptionEng(ResultCodeEngEnum.ARITHMETIC_FAILED, e);
        } else {
            return ResultVO.exception(ResultCodeEnum.ARITHMETIC_FAILED, e);
        }
    }

    /**
     * 数组越界异常
     *
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler({ArrayIndexOutOfBoundsException.class})
    public ResultVO arrayIndexOutOfBoundsExceptionHandler(ArrayIndexOutOfBoundsException e, HttpServletRequest request) {
        System.err.println(request.getHeader("lang"));
        if (StrUtil.isBlank(request.getHeader("lang"))) {
            return ResultVO.exception(ResultCodeEnum.ARRAY_INDEX_OUT_OF_BOUNDS_FAILED, e);
        } else if (StrUtil.equals("zh-CN", request.getHeader("lang"))) {
            return ResultVO.exception(ResultCodeEnum.ARRAY_INDEX_OUT_OF_BOUNDS_FAILED, e);
        } else if (StrUtil.equals("en-US", request.getHeader("lang"))) {
            return ResultVO.exceptionEng(ResultCodeEngEnum.ARRAY_INDEX_OUT_OF_BOUNDS_FAILED, e);
        } else {
            return ResultVO.exception(ResultCodeEnum.ARRAY_INDEX_OUT_OF_BOUNDS_FAILED, e);
        }
    }


    /**
     * 未知异常（指的是代码还没处理到的受检异常）
     *
     * @param e
     * @return
     */
    @ExceptionHandler({Exception.class})
    public ResultVO otherExceptionHandle(Exception e, HttpServletRequest request) {
        log.error("otherExceptionHandle", e);
        System.err.println(request.getHeader("lang"));
        if (StrUtil.isBlank(request.getHeader("lang"))) {
            return ResultVO.exception(ResultCodeEnum.ERROR, e);
        } else if (StrUtil.equals("zh-CN", request.getHeader("lang"))) {
            return ResultVO.exception(ResultCodeEnum.ERROR, e);
        } else if (StrUtil.equals("en-US", request.getHeader("lang"))) {
            return ResultVO.exceptionEng(ResultCodeEngEnum.ERROR, e);
        } else {
            return ResultVO.exception(ResultCodeEnum.ERROR, e);
        }
    }


    //---------------正常请求，没有发生异常，但要在返回前端之前再处理一下根据lang适配不同的返回枚举---------------------------------------------------------------

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> aClass) {
        return returnType.getGenericParameterType().equals(ResultVO.class);
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter methodParameter,
                                  MediaType mediaType,
                                  Class<? extends HttpMessageConverter<?>> aClass,
                                  ServerHttpRequest serverHttpRequest,
                                  ServerHttpResponse serverHttpResponse) {
        ResultVO resultVo = (ResultVO) body;

        System.err.println(serverHttpRequest.getHeaders().toString());
        List<String> langHeaders = serverHttpRequest.getHeaders().get("lang");
        if (CollectionUtil.isNotEmpty(langHeaders)) {
            System.err.println(langHeaders);
            if (StrUtil.isNotBlank(langHeaders.get(0))) {
                resultVo.setLang(langHeaders.get(0));
            }
        }

        if (StrUtil.equals(resultVo.getLang(), "en-US")) {
            resultVo.setMsg(ResultCodeEngEnum.getEngMsgByCode(resultVo.getCode()));
            return resultVo;
        } else {
            return body;
        }
    }


}
